﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Management;

namespace System.Peppers.Wmi
{
    /// <summary>
    /// The Win32_Service WMI class represents a service on a computer system running Windows. A service application conforms to the interface rules of the Service Control Manager (SCM), and can be started by a user automatically at system start through the Services control panel utility, or by an application that uses the service functions included in the Windows API. Services can start when there are no users logged on to the computer.
    /// </summary>
    public sealed class Service : CimSetting
    {
        #region Static
        private static class WmiProperties
        {
            public const string AcceptPause = "AcceptPause",
                AcceptStop = "AcceptStop",
                CheckPoint = "CheckPoint",
                DesktopInteract = "DesktopInteract",
                DisplayName = "DisplayName",
                ErrorControl = "ErrorControl",
                ExitCode = "ExitCode",
                Name = "Name",
                PathName = "PathName",
                ProcessId = "ProcessId",
                ServiceSpecificExitCode = "ServiceSpecificExitCode",
                ServiceType = "ServiceType",
                Started = "Started",
                StartMode = "StartMode",
                StartName = "StartName",
                State = "State",
                SystemName = "SystemName",
                TagId = "TagId",
                WaitHint = "WaitHint";
        }

        private static class WmiMethods
        {
            public const string StartService = "StartService",
                StopService = "StopService",
                PauseService = "PauseService",
                ResumeService = "ResumeService",
                InterrogateService = "InterrogateService",
                UserControlService = "UserControlService",
                Create = "Create",
                Change = "Change",
                ChangeStartMode = "ChangeStartMode",
                Delete = "Delete",
                GetSecurityDescriptor = "GetSecurityDescriptor",
                SetSecurityDescriptor = "SetSecurityDescriptor";
        }

        private static class WmiParameters
        {
            public const string Descriptor = "Descriptor",
                ControlCode = "ControlCode",
                Name = "Name",
                DisplayName = "DisplayName",
                PathName = "PathName",
                ServiceType = "ServiceType",
                ErrorControl = "ErrorControl",
                StartMode = "StartMode",
                DesktopInteract = "DesktopInteract",
                StartName = "StartName",
                StartPassword = "StartPassword",
                LoadOrderGroup = "LoadOrderGroup",
                LoadOrderGroupDependencies = "LoadOrderGroupDependencies",
                ServiceDependencies = "ServiceDependencies";
        }

        private static class Util
        {
            public static ServiceErrorControl ToServiceErrorControl(object value)
            {
                string text = value as string;
                if (string.IsNullOrEmpty(text))
                {
                    return ServiceErrorControl.Unknown;
                }
                switch (text.ToLower())
                {
                    case "ignore":
                        return ServiceErrorControl.Ignore;
                    case "normal":
                        return ServiceErrorControl.Normal;
                    case "severe":
                        return ServiceErrorControl.Severe;
                    case "critical":
                        return ServiceErrorControl.Critical;
                    case "unknown":
                    default:
                        return ServiceErrorControl.Unknown;
                }
            }

            public static ServiceType ToServiceType(object value)
            {
                string text = value as string;
                if (string.IsNullOrEmpty(text))
                {
                    return ServiceType.Unknown;
                }
                switch (text.ToLower())
                {
                    case "kernel driver":
                        return ServiceType.KernelDriver;
                    case "file system driver":
                        return ServiceType.FileSystemDriver;
                    case "adapter":
                        return ServiceType.Adapter;
                    case "recognizer driver":
                        return ServiceType.RecognizedDriver;
                    case "own process":
                        return ServiceType.OwnProcess;
                    case "share process":
                        return ServiceType.ShareProcess;
                    case "interactive process":
                        return ServiceType.InteractiveProcess;
                    default:
                        return ServiceType.Unknown;
                }
            }

            public static ServiceStartMode ToServiceStartMode(object value)
            {
                string text = value as string;
                if (string.IsNullOrEmpty(text))
                {
                    return ServiceStartMode.Disabled;
                }
                switch (text.ToLower())
                {
                    case "boot":
                        return ServiceStartMode.Boot;
                    case "system":
                        return ServiceStartMode.System;
                    case "auto":
                        return ServiceStartMode.Auto;
                    case "manual":
                        return ServiceStartMode.Manual;
                    case "disabled":
                    default:
                        return ServiceStartMode.Disabled;
                }
            }

            public static string ToStartModeString(ServiceStartMode mode)
            {
                return mode.ToString();
            }

            public static ServiceState ToServiceState(object value)
            {
                string text = value as string;
                if (string.IsNullOrEmpty(text))
                {
                    return ServiceState.Unknown;
                }
                switch (text.ToLower())
                {
                    case "stopped":
                        return ServiceState.Stopped;
                    case "start pending":
                        return ServiceState.StartPending;
                    case "stop pending":
                        return ServiceState.StopPending;
                    case "running":
                        return ServiceState.Running;
                    case "continue pending":
                        return ServiceState.ContinuePending;
                    case "pause pending":
                        return ServiceState.PausePending;
                    case "paused":
                        return ServiceState.Paused;
                    case "unknown":
                    default:
                        return ServiceState.Unknown;
                }
            }
        }

        /// <summary>
        /// Enumerates windows service objects on the system
        /// </summary>
        /// <returns>List of windows service objects</returns>
        public static IEnumerable<Service> GetServices()
        {
            foreach (ManagementObject item in GetInstances(WmiClasses.Win32_Service))
            {
                yield return new Service(item);
            }
        }

        /// <summary>
        /// Enumerates windows service objects on a remote system
        /// </summary>
        /// <param name="remoteParams">Connection options for remote system</param>
        /// <returns>List of windows service objects</returns>
        public static IEnumerable<Service> GetServices(WmiRemoteParameters remoteParams)
        {
            foreach (ManagementObject item in GetInstances(WmiClasses.Win32_Service, remoteParams))
            {
                yield return new Service(item);
            }
        }

        /// <summary>
        /// The Create WMI class method creates a new system service. The Win32_LoadOrderGroup parameter represents a grouping of system services defining execution dependencies. The services must be initiated in the order specified by the Load Order Group, as the services are dependent on each other. These dependent services require the presence of the antecedent services to function correctly. 
        /// </summary>
        /// <param name="name">Name of the service to install to the Create method. The maximum string length is 256 characters. The Service Control Manager database preserves the case of the characters, but service name comparisons are always case-insensitive. Forward-slashes (/) and double back-slashes (\) are invalid service name characters.</param>
        /// <param name="displayName">Display name of the service. This string has a maximum length of 256 characters. The name is case-preserved in the Service Control Manager. DisplayName comparisons are always case-insensitive.</param>
        /// <param name="pathName">Fully qualified path to the executable file that implements the service.</param>
        /// <param name="serviceType">Type of services provided to processes that call them.</param>
        /// <param name="errorControl">Severity of the error if the Create method fails to start. The value indicates the action taken by the startup program if failure occurs. All errors are logged by the system.</param>
        /// <param name="startMode">Start mode of the Windows base service.</param>
        /// <param name="desktopInteract">If true, the service can create or communicate with windows on the desktop.</param>
        /// <param name="startName">Account name under which the service runs. Depending on the service type, the account name may be in the form of DomainName\Username. The service process is logged using one of these two forms when it runs. If the account belongs to the built-in domain, .\Username can be specified. If NULL is specified, the service is logged on as the LocalSystem account. For a kernel or system-level drivers, StartName contains the driver object name (that is, \FileSystem\Rdr or \Driver\Xns) which the input and output (I/O) system uses to load the device driver. If NULL is specified, the driver runs with a default object name created by the I/O system based on the service name. Example: DWDOM\Admin.</param>
        /// <param name="startPassword">Password to the account name specified by the StartName parameter. Specify NULL if you are not changing the password. Specify an empty string if the service has no password.</param>
        /// <returns>Result of method</returns>
        public static ServiceResult Create(string name, string displayName, string pathName, ServiceType serviceType, ServiceErrorControl errorControl, ServiceStartMode startMode, bool desktopInteract, string startName, string startPassword)
        {
            return Create(name, displayName, pathName, serviceType, errorControl, startMode, desktopInteract, startName, startPassword);
        }

        /// <summary>
        /// The Create WMI class method creates a new system service. The Win32_LoadOrderGroup parameter represents a grouping of system services defining execution dependencies. The services must be initiated in the order specified by the Load Order Group, as the services are dependent on each other. These dependent services require the presence of the antecedent services to function correctly. 
        /// </summary>
        /// <param name="name">Name of the service to install to the Create method. The maximum string length is 256 characters. The Service Control Manager database preserves the case of the characters, but service name comparisons are always case-insensitive. Forward-slashes (/) and double back-slashes (\) are invalid service name characters.</param>
        /// <param name="displayName">Display name of the service. This string has a maximum length of 256 characters. The name is case-preserved in the Service Control Manager. DisplayName comparisons are always case-insensitive.</param>
        /// <param name="pathName">Fully qualified path to the executable file that implements the service.</param>
        /// <param name="serviceType">Type of services provided to processes that call them.</param>
        /// <param name="errorControl">Severity of the error if the Create method fails to start. The value indicates the action taken by the startup program if failure occurs. All errors are logged by the system.</param>
        /// <param name="startMode">Start mode of the Windows base service.</param>
        /// <param name="desktopInteract">If true, the service can create or communicate with windows on the desktop.</param>
        /// <param name="startName">Account name under which the service runs. Depending on the service type, the account name may be in the form of DomainName\Username. The service process is logged using one of these two forms when it runs. If the account belongs to the built-in domain, .\Username can be specified. If NULL is specified, the service is logged on as the LocalSystem account. For a kernel or system-level drivers, StartName contains the driver object name (that is, \FileSystem\Rdr or \Driver\Xns) which the input and output (I/O) system uses to load the device driver. If NULL is specified, the driver runs with a default object name created by the I/O system based on the service name. Example: DWDOM\Admin.</param>
        /// <param name="startPassword">Password to the account name specified by the StartName parameter. Specify NULL if you are not changing the password. Specify an empty string if the service has no password.</param>
        /// <param name="loadOrderGroup">Group name associated with the new service. Load order groups are contained in the registry, and determine the sequence in which services are loaded into the operating system. If the pointer is NULL or if it points to an empty string, the service does not belong to a group. Dependencies between groups should be listed in the LoadOrderGroupDependencies parameter. Services in the load-ordering group list are started first, followed by services in groups not in the load-ordering group list, followed by services that do not belong to a group. The registry has a list of load ordering groups located at: HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\ServiceGroupOrder</param>
        /// <param name="loadOrderGroupDependencies">Array of load-ordering groups that must start before this service. Each item in the array is delimited by NULL and the list is terminated by two NULL values. In Visual Basic or script you can pass a vbArray. If the pointer is NULL or if it points to an empty string, the service has no dependencies. Group names must be prefixed by the SC_GROUP_IDENTIFIER (defined in the Winsvc.h file) character to differentiate it from a service name, because services and service groups share the same namespace. Dependency on a group means that this service can run if at least one member of the group is running after an attempt to start all of the members of the group.</param>
        /// <param name="serviceDependencies">Array that contains names of services that must start before this service starts. Each item in the array is delimited by NULL and the list is terminated by two NULL values. In Visual Basic or script you can pass a vbArray. If the pointer is NULL, or if it points to an empty string, the service has no dependencies. Dependency on a service means that this service can only run if the service it depends on is running.</param>
        /// <returns>Result of method</returns>
        public static ServiceResult Create(string name, string displayName, string pathName, ServiceType serviceType, ServiceErrorControl errorControl, ServiceStartMode startMode, bool desktopInteract, string startName, string startPassword, string loadOrderGroup, string[] loadOrderGroupDependencies, string[] serviceDependencies)
        {
            Service service = new Service(GetStaticInstance(WmiClasses.Win32_Service));
            return (ServiceResult)service.ExecuteMethodWithResult(WmiMethods.Change, delegate(ManagementBaseObject inParam)
            {
                inParam[WmiParameters.Name] = name;
                inParam[WmiParameters.DisplayName] = displayName;
                inParam[WmiParameters.PathName] = pathName;
                inParam[WmiParameters.ServiceType] = serviceType;
                inParam[WmiParameters.ErrorControl] = errorControl;
                inParam[WmiParameters.StartMode] = Util.ToStartModeString(startMode);
                inParam[WmiParameters.DesktopInteract] = desktopInteract;
                inParam[WmiParameters.StartName] = startName;
                inParam[WmiParameters.StartPassword] = startPassword;
                inParam[WmiParameters.LoadOrderGroup] = loadOrderGroup;
                inParam[WmiParameters.LoadOrderGroupDependencies] = loadOrderGroupDependencies;
                inParam[WmiParameters.ServiceDependencies] = serviceDependencies;
            });
        }

        /// <summary>
        /// The Create WMI class method creates a new system service on a remote system. The Win32_LoadOrderGroup parameter represents a grouping of system services defining execution dependencies. The services must be initiated in the order specified by the Load Order Group, as the services are dependent on each other. These dependent services require the presence of the antecedent services to function correctly. 
        /// </summary>
        /// <param name="remoteParams">Connection options for remote system</param>
        /// <param name="name">Name of the service to install to the Create method. The maximum string length is 256 characters. The Service Control Manager database preserves the case of the characters, but service name comparisons are always case-insensitive. Forward-slashes (/) and double back-slashes (\) are invalid service name characters.</param>
        /// <param name="displayName">Display name of the service. This string has a maximum length of 256 characters. The name is case-preserved in the Service Control Manager. DisplayName comparisons are always case-insensitive.</param>
        /// <param name="pathName">Fully qualified path to the executable file that implements the service.</param>
        /// <param name="serviceType">Type of services provided to processes that call them.</param>
        /// <param name="errorControl">Severity of the error if the Create method fails to start. The value indicates the action taken by the startup program if failure occurs. All errors are logged by the system.</param>
        /// <param name="startMode">Start mode of the Windows base service.</param>
        /// <param name="desktopInteract">If true, the service can create or communicate with windows on the desktop.</param>
        /// <param name="startName">Account name under which the service runs. Depending on the service type, the account name may be in the form of DomainName\Username. The service process is logged using one of these two forms when it runs. If the account belongs to the built-in domain, .\Username can be specified. If NULL is specified, the service is logged on as the LocalSystem account. For a kernel or system-level drivers, StartName contains the driver object name (that is, \FileSystem\Rdr or \Driver\Xns) which the input and output (I/O) system uses to load the device driver. If NULL is specified, the driver runs with a default object name created by the I/O system based on the service name. Example: DWDOM\Admin.</param>
        /// <param name="startPassword">Password to the account name specified by the StartName parameter. Specify NULL if you are not changing the password. Specify an empty string if the service has no password.</param>
        /// <returns>Result of method</returns>
        public static ServiceResult Create(WmiRemoteParameters remoteParams, string name, string displayName, string pathName, ServiceType serviceType, ServiceErrorControl errorControl, ServiceStartMode startMode, bool desktopInteract, string startName, string startPassword)
        {
            return Create(remoteParams, name, displayName, pathName, serviceType, errorControl, startMode, desktopInteract, startName, startPassword, null, null, null);
        }

        /// <summary>
        /// The Create WMI class method creates a new system service on a remote system. The Win32_LoadOrderGroup parameter represents a grouping of system services defining execution dependencies. The services must be initiated in the order specified by the Load Order Group, as the services are dependent on each other. These dependent services require the presence of the antecedent services to function correctly. 
        /// </summary>
        /// <param name="remoteParams">Connection options for remote system</param>
        /// <param name="name">Name of the service to install to the Create method. The maximum string length is 256 characters. The Service Control Manager database preserves the case of the characters, but service name comparisons are always case-insensitive. Forward-slashes (/) and double back-slashes (\) are invalid service name characters.</param>
        /// <param name="displayName">Display name of the service. This string has a maximum length of 256 characters. The name is case-preserved in the Service Control Manager. DisplayName comparisons are always case-insensitive.</param>
        /// <param name="pathName">Fully qualified path to the executable file that implements the service.</param>
        /// <param name="serviceType">Type of services provided to processes that call them.</param>
        /// <param name="errorControl">Severity of the error if the Create method fails to start. The value indicates the action taken by the startup program if failure occurs. All errors are logged by the system.</param>
        /// <param name="startMode">Start mode of the Windows base service.</param>
        /// <param name="desktopInteract">If true, the service can create or communicate with windows on the desktop.</param>
        /// <param name="startName">Account name under which the service runs. Depending on the service type, the account name may be in the form of DomainName\Username. The service process is logged using one of these two forms when it runs. If the account belongs to the built-in domain, .\Username can be specified. If NULL is specified, the service is logged on as the LocalSystem account. For a kernel or system-level drivers, StartName contains the driver object name (that is, \FileSystem\Rdr or \Driver\Xns) which the input and output (I/O) system uses to load the device driver. If NULL is specified, the driver runs with a default object name created by the I/O system based on the service name. Example: DWDOM\Admin.</param>
        /// <param name="startPassword">Password to the account name specified by the StartName parameter. Specify NULL if you are not changing the password. Specify an empty string if the service has no password.</param>
        /// <param name="loadOrderGroup">Group name associated with the new service. Load order groups are contained in the registry, and determine the sequence in which services are loaded into the operating system. If the pointer is NULL or if it points to an empty string, the service does not belong to a group. Dependencies between groups should be listed in the LoadOrderGroupDependencies parameter. Services in the load-ordering group list are started first, followed by services in groups not in the load-ordering group list, followed by services that do not belong to a group. The registry has a list of load ordering groups located at: HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\ServiceGroupOrder</param>
        /// <param name="loadOrderGroupDependencies">Array of load-ordering groups that must start before this service. Each item in the array is delimited by NULL and the list is terminated by two NULL values. In Visual Basic or script you can pass a vbArray. If the pointer is NULL or if it points to an empty string, the service has no dependencies. Group names must be prefixed by the SC_GROUP_IDENTIFIER (defined in the Winsvc.h file) character to differentiate it from a service name, because services and service groups share the same namespace. Dependency on a group means that this service can run if at least one member of the group is running after an attempt to start all of the members of the group.</param>
        /// <param name="serviceDependencies">Array that contains names of services that must start before this service starts. Each item in the array is delimited by NULL and the list is terminated by two NULL values. In Visual Basic or script you can pass a vbArray. If the pointer is NULL, or if it points to an empty string, the service has no dependencies. Dependency on a service means that this service can only run if the service it depends on is running.</param>
        /// <returns>Result of method</returns>
        public static ServiceResult Create(WmiRemoteParameters remoteParams, string name, string displayName, string pathName, ServiceType serviceType, ServiceErrorControl errorControl, ServiceStartMode startMode, bool desktopInteract, string startName, string startPassword, string loadOrderGroup, string[] loadOrderGroupDependencies, string[] serviceDependencies)
        {
            using (Service service = new Service(GetStaticInstance(WmiClasses.Win32_Service, remoteParams)))
            {
                return (ServiceResult)service.ExecuteMethodWithResult(WmiMethods.Change, delegate(ManagementBaseObject inParam)
                {
                    inParam[WmiParameters.Name] = name;
                    inParam[WmiParameters.DisplayName] = displayName;
                    inParam[WmiParameters.PathName] = pathName;
                    inParam[WmiParameters.ServiceType] = serviceType;
                    inParam[WmiParameters.ErrorControl] = errorControl;
                    inParam[WmiParameters.StartMode] = Util.ToStartModeString(startMode);
                    inParam[WmiParameters.DesktopInteract] = desktopInteract;
                    inParam[WmiParameters.StartName] = startName;
                    inParam[WmiParameters.StartPassword] = startPassword;
                    inParam[WmiParameters.LoadOrderGroup] = loadOrderGroup;
                    inParam[WmiParameters.LoadOrderGroupDependencies] = loadOrderGroupDependencies;
                    inParam[WmiParameters.ServiceDependencies] = serviceDependencies;
                });
            }
        }
        #endregion

        internal Service(ManagementObject wmiObject)
            : base(wmiObject)
        { }

        /// <summary>
        /// Service can be paused.
        /// </summary>
        public bool AcceptPause
        {
            get { return Convert.ToBoolean(this[WmiProperties.AcceptPause] ?? false); }
        }

        /// <summary>
        /// Service can be stopped.
        /// </summary>
        public bool AcceptStop
        {
            get { return Convert.ToBoolean(this[WmiProperties.AcceptStop] ?? false); }
        }

        /// <summary>
        /// Value that the service increments periodically to report its progress during a long start, stop, pause, or continue operation. For example, the service increments this value as it completes each step of its initialization when it is starting up. The user interface program that invokes the operation on the service uses this value to track the progress of the service during a lengthy operation. This value is not valid and should be zero when the service does not have a start, stop, pause, or continue operation pending.
        /// </summary>
        public uint CheckPoint
        {
            get { return Convert.ToUInt32(this[WmiProperties.CheckPoint] ?? false); }
        }

        /// <summary>
        /// Service can create or communicate with windows on the desktop.
        /// </summary>
        public bool DesktopInteract
        {
            get { return Convert.ToBoolean(this[WmiProperties.DesktopInteract] ?? false); }
        }

        /// <summary>
        /// Display name of the service. This string has a maximum length of 256 characters. The name is case-preserved in the Service Control Manager. However, DisplayName comparisons are always case-insensitive.
        /// </summary>
        public string DisplayName
        {
            get { return Convert.ToString(this[WmiProperties.DisplayName]); }
        }

        /// <summary>
        /// Windows error code that defines errors encountered in starting or stopping the service. This property is set to ERROR_SERVICE_SPECIFIC_ERROR (1066) when the error is unique to the service represented by this class, and information about the error is available in the ServiceSpecificExitCode property. The service sets this value to NO_ERROR when running, and again upon normal termination.
        /// </summary>
        public uint ExitCode
        {
            get { return Convert.ToUInt32(this[WmiProperties.ExitCode] ?? false); }
        }

        /// <summary>
        /// Unique identifier of the service that provides an indication of the functionality that is managed. This functionality is described in the Description property of the object.
        /// </summary>
        public string Name
        {
            get { return Convert.ToString(this[WmiProperties.Name]); }
        }

        /// <summary>
        /// Fully-qualified path to the service binary file that implements the service.  
        /// Example: "\SystemRoot\System32\drivers\afd.sys"
        /// </summary>
        public string PathName
        {
            get { return Convert.ToString(this[WmiProperties.PathName]); }
        }

        /// <summary>
        /// Process identifier of the service.
        /// </summary>
        public uint ProcessId
        {
            get { return Convert.ToUInt32(this[WmiProperties.ProcessId] ?? false); }
        }

        /// <summary>
        /// Service-specific error code for errors that occur while the service is either starting or stopping. The exit codes are defined by the service represented by this class. This value is only set when the ExitCode property value is ERROR_SERVICE_SPECIFIC_ERROR (1066).
        /// </summary>
        public uint ServiceSpecificExitCode
        {
            get { return Convert.ToUInt32(this[WmiProperties.ServiceSpecificExitCode] ?? false); }
        }

        /// <summary>
        /// Type of service provided to calling processes.
        /// </summary>
        public ServiceType ServiceType
        {
            get { return Util.ToServiceType(this[WmiProperties.ServiceType]); }
        }

        /// <summary>
        /// Service has been started.
        /// </summary>
        public bool Started
        {
            get { return Convert.ToBoolean(this[WmiProperties.Started] ?? false); }
        }

        /// <summary>
        /// Start mode of the Windows base service.
        /// </summary>
        public ServiceStartMode StartMode
        {
            get { return Util.ToServiceStartMode(this[WmiProperties.StartMode]); }
        }

        /// <summary>
        /// Severity of the error if this service fails to start during startup. The value indicates the action taken by the startup program if failure occurs. All errors are logged by the computer system.
        /// </summary>
        public ServiceErrorControl ErrorControl
        {
            get { return Util.ToServiceErrorControl(this[WmiProperties.ErrorControl]); }
        }

        /// <summary>
        /// Account name under which a service runs. Depending on the service type, the account name may be in the form of DomainName\Username. The service process is logged by using one of these two forms when it runs. If the account belongs to the built-in domain, then .\Username can be specified. For kernel or system-level drivers, StartName contains the driver object name (that is, \FileSystem\Rdr or \Driver\Xns) which the I/O system uses to load the device driver. Additionally, if NULL is specified, the driver runs with a default object name created by the I/O system based on the service name.
        /// </summary>
        public string StartName
        {
            get { return Convert.ToString(this[WmiProperties.StartName]); }
        }

        /// <summary>
        /// Current state of the base service.
        /// </summary>
        public ServiceState State
        {
            get { return Util.ToServiceState(this[WmiProperties.State]); }
        }

        /// <summary>
        /// Name of the system that hosts this service.
        /// </summary>
        public string SystemName
        {
            get { return Convert.ToString(this[WmiProperties.SystemName]); }
        }

        /// <summary>
        /// Unique tag value for this service in the group. A value of 0 (zero) indicates that the service does not have a tag. A tag can be used to order service startup within a load order group by specifying a tag order vector in the registry located at:
        /// HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\     GroupOrderList
        /// Tags are only evaluated for Kernel Driver and File System Driver start type services that have Boot or System start modes.
        /// </summary>
        public uint TagId
        {
            get { return Convert.ToUInt32(this[WmiProperties.TagId] ?? false); }
        }

        /// <summary>
        /// Estimated time required, in milliseconds, for a pending start, stop, pause, or continue operation. After the specified time has elapsed, the service makes its next call to the SetServiceStatus method with either an incremented CheckPoint value or a change in CurrentState. If the amount of time specified by WaitHint passes, and CheckPoint has not been incremented, or CurrentState has not changed, the service control manager or service control program assumes that an error has occurred.
        /// </summary>
        public uint WaitHint
        {
            get { return Convert.ToUInt32(this[WmiProperties.WaitHint] ?? false); }
        }

        /// <summary>
        /// The StartService method attempts to place the referenced service into its startup state.
        /// </summary>
        /// <returns>Result of method</returns>
        public ServiceResult Start()
        {
            return (ServiceResult)ExecuteMethodWithResult(WmiMethods.StartService, null);
        }

        /// <summary>
        /// The StopService WMI class method places the service, represented by the Win32_Service object, in the stopped state.
        /// </summary>
        /// <returns>Result of method</returns>
        public ServiceResult Stop()
        {
            return (ServiceResult)ExecuteMethodWithResult(WmiMethods.StopService, null);
        }

        /// <summary>
        /// The PauseService WMI class method attempts to place the service in the paused state.
        /// </summary>
        /// <returns>Result of method</returns>
        public ServiceResult Pause()
        {
            return (ServiceResult)ExecuteMethodWithResult(WmiMethods.PauseService, null);
        }

        /// <summary>
        /// The ResumeService WMI class method attempts to place the referenced service in the resumed state.
        /// </summary>
        /// <returns>Result of method</returns>
        public ServiceResult Resume()
        {
            return (ServiceResult)ExecuteMethodWithResult(WmiMethods.ResumeService, null);
        }

        /// <summary>
        /// The InterrogateService WMI class method requests that the referenced service update its state to the service manager.
        /// </summary>
        /// <returns>Result of method</returns>
        public ServiceResult Interrogate()
        {
            return (ServiceResult)ExecuteMethodWithResult(WmiMethods.InterrogateService, null);
        }

        /// <summary>
        /// The Delete WMI class method deletes an existing service.
        /// </summary>
        /// <returns>Result of method</returns>
        public ServiceResult Delete()
        {
            return (ServiceResult)ExecuteMethodWithResult(WmiMethods.Delete, null);
        }

        /// <summary>
        /// The UserControlService WMI class method attempts to send a user-defined control code to the referenced service. 
        /// </summary>
        /// <param name="controlCode">Specifies defined values (from 128 to 255) that provide control commands specific to a user.</param>
        /// <returns>Result of method</returns>
        public ServiceResult UserControlService(byte controlCode)
        {
            return (ServiceResult)ExecuteMethodWithResult(WmiMethods.UserControlService, delegate(ManagementBaseObject inParam)
            {
                inParam[WmiParameters.ControlCode] = controlCode;
            });
        }

        /// <summary>
        /// The SetSecurityDescriptor method writes an updated version of the security descriptor that controls access to the service.
        /// </summary>
        /// <param name="descriptor">The security descriptor associated with the service.</param>
        /// <returns>Result of method</returns>
        public ServiceResult SetSecurityDescriptor(WmiSecurityDescriptor descriptor)
        {
            return (ServiceResult)ExecuteMethodWithResult(WmiMethods.SetSecurityDescriptor, delegate(ManagementBaseObject inParam)
            {
                inParam[WmiParameters.Descriptor] = descriptor.WmiInstance;
            });
        }

        /// <summary>
        /// The GetSecurityDescriptor method returns the security descriptor that controls access to the service.
        /// </summary>
        /// <param name="descriptor">The security descriptor associated with the service.</param>
        /// <returns>Result of method</returns>
        public ServiceResult GetSecurityDescriptor(out WmiSecurityDescriptor descriptor)
        {
            using (ManagementBaseObject outParam = ExecuteMethod(WmiMethods.GetSecurityDescriptor, null))
            {
                descriptor = new WmiSecurityDescriptor(outParam[WmiParameters.Descriptor] as ManagementObject);
                return (ServiceResult)Convert.ToUInt32(outParam[WmiClasses.ReturnValue]);
            }
        }

        /// <summary>
        /// The ChangeStartMode WMI class method modifies the start mode of a Win32_Service.
        /// </summary>
        /// <param name="startMode">Start mode of the Windows base service.</param>
        /// <returns>Result of method</returns>
        public ServiceResult Change(ServiceStartMode startMode)
        {
            return (ServiceResult)ExecuteMethodWithResult(WmiMethods.ChangeStartMode, delegate(ManagementBaseObject inParam)
            {
                inParam[WmiParameters.StartMode] = startMode;
            });
        }

        /// <summary>
        /// The Change WMI class method modifies a Win32_Service. The Win32_LoadOrderGroup parameter represents a group of system services that define execution dependencies. The services must be initiated in the order specified by the Load Order Group because the services depend on each other. These dependent services require the presence of the antecedent services to function correctly.
        /// </summary>
        /// <param name="displayName">The display name of the service. This string has a maximum length of 256 characters. The name is case-preserved in the service control manager. DisplayName comparisons are always case-insensitive.</param>
        /// <param name="pathName">The fully-qualified path to the executable file that implements the service, for example, "\SystemRoot\System32\drivers\afd.sys".</param>
        /// <param name="serviceType">The type of services provided to processes that call them.</param>
        /// <param name="errorControl">Severity of the error if this service fails to start during startup. The value indicates the action taken by the startup program if failure occurs. All errors are logged by the system.</param>
        /// <param name="startMode">Start mode of the Windows base service.</param>
        /// <param name="desktopInteract">If true, the service can create or communicate with a window on the desktop.</param>
        /// <param name="startName">Account name the service runs under. Depending on the service type, the account name may be in the form of DomainName\Username or .\Username. The service process will be logged using one of these two forms when it runs. If the account belongs to the built-in domain, .\Username can be specified. If NULL is specified, the service will be logged on as the LocalSystem account. For kernel or system-level drivers, StartName contains the driver object name (that is, \FileSystem\Rdr or \Driver\Xns) that the input and output (I/O) system uses to load the device driver. If NULL is specified, the driver runs with a default object name created by the I/O system based on the service name, for example, "DWDOM\Admin".</param>
        /// <param name="startPassword">Password to the account name specified by the StartName parameter. Specify NULL if you are not changing the password. Specify an empty string if the service has no password.</param>
        /// <returns>Result of method</returns>
        public ServiceResult Change(string displayName, string pathName, ServiceType serviceType, ServiceErrorControl errorControl, ServiceStartMode startMode, bool desktopInteract, string startName, string startPassword)
        {
            return Change(displayName, pathName, serviceType, errorControl, startMode, desktopInteract, startName, startPassword, null, null, null);
        }

        /// <summary>
        /// The Change WMI class method modifies a Win32_Service. The Win32_LoadOrderGroup parameter represents a group of system services that define execution dependencies. The services must be initiated in the order specified by the Load Order Group because the services depend on each other. These dependent services require the presence of the antecedent services to function correctly.
        /// </summary>
        /// <param name="displayName">The display name of the service. This string has a maximum length of 256 characters. The name is case-preserved in the service control manager. DisplayName comparisons are always case-insensitive.</param>
        /// <param name="pathName">The fully-qualified path to the executable file that implements the service, for example, "\SystemRoot\System32\drivers\afd.sys".</param>
        /// <param name="serviceType">The type of services provided to processes that call them.</param>
        /// <param name="errorControl">Severity of the error if this service fails to start during startup. The value indicates the action taken by the startup program if failure occurs. All errors are logged by the system.</param>
        /// <param name="startMode">Start mode of the Windows base service.</param>
        /// <param name="desktopInteract">If true, the service can create or communicate with a window on the desktop.</param>
        /// <param name="startName">Account name the service runs under. Depending on the service type, the account name may be in the form of DomainName\Username or .\Username. The service process will be logged using one of these two forms when it runs. If the account belongs to the built-in domain, .\Username can be specified. If NULL is specified, the service will be logged on as the LocalSystem account. For kernel or system-level drivers, StartName contains the driver object name (that is, \FileSystem\Rdr or \Driver\Xns) that the input and output (I/O) system uses to load the device driver. If NULL is specified, the driver runs with a default object name created by the I/O system based on the service name, for example, "DWDOM\Admin".</param>
        /// <param name="startPassword">Password to the account name specified by the StartName parameter. Specify NULL if you are not changing the password. Specify an empty string if the service has no password.</param>
        /// <param name="loadOrderGroup">Group name that it is associated with. Load order groups are contained in the system registry, and determine the sequence in which services are loaded into the operating system. If the pointer is NULL, or if it points to an empty string, the service does not belong to a group. Dependencies between groups should be listed in the LoadOrderGroupDependencies parameter. Services in the load-ordering group list are started first, followed by services in groups not in the load-ordering group list, followed by services that do not belong to a group. The system registry has a list of load ordering groups located at HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\ServiceGroupOrder.</param>
        /// <param name="loadOrderGroupDependencies">List of load-ordering groups that must start before this service starts. The array is doubly null-terminated. If the pointer is NULL, or if it points to an empty string, the service has no dependencies. Group names must be prefixed by the SC_GROUP_IDENTIFIER (defined in the Winsvc.h file) character to differentiate them from service names because services and service groups share the same name space. Dependency on a group means that this service can run if at least one member of the group is running after an attempt to start all of the members of the group.</param>
        /// <param name="serviceDependencies">List that contains the names of services that must start before this service starts. The array is doubly NULL-terminated. If the pointer is NULL, or if it points to an empty string, the service has no dependencies. Dependency on a service indicates that this service can run only if the service it depends on is running.</param>
        /// <returns>Result of method</returns>
        public ServiceResult Change(string displayName, string pathName, ServiceType serviceType, ServiceErrorControl errorControl, ServiceStartMode startMode, bool desktopInteract, string startName, string startPassword, string loadOrderGroup, string[] loadOrderGroupDependencies, string[] serviceDependencies)
        {
            return (ServiceResult)ExecuteMethodWithResult(WmiMethods.Change, delegate(ManagementBaseObject inParam)
            {
                inParam[WmiParameters.DisplayName] = displayName;
                inParam[WmiParameters.PathName] = pathName;
                inParam[WmiParameters.ServiceType] = serviceType;
                inParam[WmiParameters.ErrorControl] = errorControl;
                inParam[WmiParameters.StartMode] = Util.ToStartModeString(startMode);
                inParam[WmiParameters.DesktopInteract] = desktopInteract;
                inParam[WmiParameters.StartName] = startName;
                inParam[WmiParameters.StartPassword] = startPassword;
                inParam[WmiParameters.LoadOrderGroup] = loadOrderGroup;
                inParam[WmiParameters.LoadOrderGroupDependencies] = loadOrderGroupDependencies;
                inParam[WmiParameters.ServiceDependencies] = serviceDependencies;
            });
        }
    }
}